package bancosys.tec.utils.db.executor;

import java.util.ArrayList;
import java.util.List;

/**
 * Parser para nomes e valores de colunas de um statement SQL
 * 
 * @author takeshi
 */
public class SQLColumnParser {

    private final char[] toParse;

    private int currentPosition;

    /**
     * C'tor
     * 
     * @param raw a string raw
     */
    public SQLColumnParser(String raw) {
        this.toParse = raw.toCharArray();
    }

    /**
     * Faz o parse
     * 
     * @param raw String
     * @return String[]
     */
    public static String[] parse(String raw) {
        return new SQLColumnParser(raw).parseColumns();
    }

    /**
     * @return String[]
     */
    public String[] parseColumns() {
        List<String> cols = new ArrayList<String>();
        while (currentPosition < toParse.length) {
            String next = nextColumn(new StringBuilder());
            if (next.length() > 0) {
                cols.add(next);
            }
        }
        return cols.toArray(new String[cols.size()]);
    }

    /**
     * Faz o parse ate encontrar o final da string ou uma ','
     * 
     * @param sb the {@link StringBuilder}
     * @return String
     */
    private String nextColumn(StringBuilder sb) {
        if (currentPosition == toParse.length) {
            return sb.toString();
        }
        char c = toParse[currentPosition++];
        switch (c) {
        case ' ':
            return nextColumn(sb);

        case '\'':
            return nextColumnEscapeBlock(new StringBuilder("\'"), '\'');

        case '"':
            return nextColumnEscapeBlock(new StringBuilder("\""), '"');

        case ',':
            return sb.toString();

        default:
            sb.append(c);
            return nextColumn(sb);
        }
    }

    /**
     * Faz o parse ate encontrar o character 'end'
     * 
     * @param sb {@link StringBuilder} prefixo
     * @param end char
     * @return String
     */
    private String nextColumnEscapeBlock(StringBuilder sb, char end) {
        char c = toParse[currentPosition++];
        switch (c) {
        case '\\':
            if(toParse[currentPosition] == end) {
                //escaped ' or "
                sb.append(end);
                currentPosition++;
            } else {
                sb.append(c);
                sb.append(toParse[currentPosition++]);
            }
            return nextColumnEscapeBlock(sb, end);

        default:
            sb.append(c);
            if (end == c) {
                consumeUntil(',');
                return sb.toString();
            } else {
                return nextColumnEscapeBlock(sb, end);
            }
        }
    }

    /**
     * 'consome' o stream ateh o char c
     * 
     * @param c char
     */
    private void consumeUntil(char c) {
        while (currentPosition < toParse.length && toParse[currentPosition++] != c) {
            // just for the side effects
        }
    }

}
